﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace ServiceTemplate
{
    public class ClientSocket
    {

        public static SocketType sockType = SocketType.Stream;
        public static ProtocolType sockProtocol = ProtocolType.Tcp;
        public static string remoteName = "localhost";
        public static string textMessage = "Client: This is a test";
        public static bool udpConnect = false;
        public static int remotePort = 5150;
        public static int bufferSize = 4096;
        /// <summary>
        /// This routine repeatedly copies a string message into a byte array until filled.
        /// </summary>
        /// <param name="dataBuffer">Byte buffer to fill with string message</param>
        /// <param name="message">String message to copy</param>
        static public void FormatBuffer(byte[] dataBuffer, string message)
        {
            byte[] byteMessage = new byte[bufferSize];
            byteMessage = System.Text.Encoding.Default.GetBytes(message + "\r\n");
            int index = 0;

            // First convert the string to bytes and then copy into send buffer
            while (index < byteMessage.Length)
            {
                for (int j = 0; j < byteMessage.Length; j++)
                {
                    dataBuffer[index] = byteMessage[j];
                    index++;
                    // Make sure we don't go past the send buffer length
                    if (index >= dataBuffer.Length)
                    {
                        break;
                    }
                }
            }
            byteMessage[bufferSize - 3] = 0x0D;
            byteMessage[bufferSize - 2] = 0x0A;
        }

        /// <summary>
        /// Prints simple usage information.
        /// </summary>
        static public void usage()
        {
            Console.WriteLine("usage: Executable_file_name [-c] [-n server] [-p port] [-m message]");
            Console.WriteLine("                        [-t tcp|udp] [-x size]");
            Console.WriteLine("     -c              If UDP connect the socket before sending");
            Console.WriteLine("     -n server       Server name or address to connect/send to");
            Console.WriteLine("     -p port         Port number to connect/send to");
            Console.WriteLine("     -m message      String message to format in request buffer");
            Console.WriteLine("     -t tcp|udp      Indicates to use either the TCP or UDP protocol");
            Console.WriteLine("     -x size         Size of send and receive buffers");
            Console.WriteLine(" Else, default values will be used...");
        }

        /// <summary>
        /// This is the main function for the simple client. It parses the command line and creates
        /// a socket of the requested type. For TCP, it will resolve the name and attempt to connect
        /// to each resolved address until a successful connection is made. Once connected a request
        /// message is sent followed by shutting down the send connection. The client then receives
        /// data until the server closes its side at which point the client socket is closed. For
        /// UDP, the socket is created and if indicated connected to the server's address. A single
        /// request datagram message. The client then waits to receive a response and continues to
        /// do so until a zero byte datagram is receive which indicates the end of the response.
        /// </summary>
        /// <param name="args">Command line arguments</param>
        public ClientSocket(string[] args)
        {



            // Parse the command line
            for (int i = 0; i < args.Length; i++)
            {
                try
                {
                    if ((args[i][0] == '-') || (args[i][0] == '/'))
                    {
                        switch (Char.ToLower(args[i][1]))
                        {
                            case 'c':       // "Connect" the UDP socket to the destination
                                udpConnect = true;
                                break;
                            case 'n':       // Destination address to connect to or send to
                                remoteName = args[++i];
                                break;
                            case 'm':       // Text message to put into the send buffer
                                textMessage = args[++i];
                                break;
                            case 'p':       // Port number for the destination
                                remotePort = System.Convert.ToInt32(args[++i]);
                                break;
                            case 't':       // Specified TCP or UDP
                                i++;
                                if (String.Compare(args[i], "tcp", true) == 0)
                                {
                                    sockType = SocketType.Stream;
                                    sockProtocol = ProtocolType.Tcp;
                                }
                                else if (String.Compare(args[i], "udp", true) == 0)
                                {
                                    sockType = SocketType.Dgram;
                                    sockProtocol = ProtocolType.Udp;
                                }
                                else
                                {
                                    usage();
                                    return;
                                }
                                break;
                            case 'x':       // Size of the send and receive buffers
                                bufferSize = System.Convert.ToInt32(args[++i]) + 2;
                                break;
                            default:
                                usage();
                                return;
                        }
                    }
                }
                catch
                {
                    usage();
                    return;
                }
            }

        }

        public void Send(string[] args)
        {


            // Parse the command line
            for (int i = 0; i < args.Length; i++)
            {
                try
                {
                    if ((args[i][0] == '-') || (args[i][0] == '/'))
                    {
                        switch (Char.ToLower(args[i][1]))
                        {
                            case 'c':       // "Connect" the UDP socket to the destination
                                udpConnect = true;
                                break;
                            case 'n':       // Destination address to connect to or send to
                                remoteName = args[++i];
                                break;
                            case 'm':       // Text message to put into the send buffer
                                textMessage = args[++i];
                                break;
                            case 'p':       // Port number for the destination
                                remotePort = System.Convert.ToInt32(args[++i]);
                                break;
                            case 't':       // Specified TCP or UDP
                                i++;
                                if (String.Compare(args[i], "tcp", true) == 0)
                                {
                                    sockType = SocketType.Stream;
                                    sockProtocol = ProtocolType.Tcp;
                                }
                                else if (String.Compare(args[i], "udp", true) == 0)
                                {
                                    sockType = SocketType.Dgram;
                                    sockProtocol = ProtocolType.Udp;
                                }
                                else
                                {
                                    usage();
                                    return;
                                }
                                break;
                            case 'x':       // Size of the send and receive buffers
                                bufferSize = System.Convert.ToInt32(args[++i]) + 2;
                                break;
                            default:
                                //  usage();
                                break;
                        }
                    }
                }
                catch
                {
                    //   usage();
                    return;
                }
            }
            Socket clientSocket = null;
            IPHostEntry resolvedHost = null;
            IPEndPoint destination = null;
            byte[] sendBuffer = new byte[bufferSize], recvBuffer = new Byte[bufferSize];
            int rc;

            // Format the string message into the send buffer
            FormatBuffer(sendBuffer, textMessage);

            try
            {
                // Try to resolve the remote host name or address
                resolvedHost = Dns.GetHostEntry(remoteName);
                Console.WriteLine("Client: GetHostEntry() is OK...");

                // Try each address returned
                foreach (IPAddress addr in resolvedHost.AddressList)
                {
                    // Create a socket corresponding to the address family of the resolved address
                    clientSocket = new Socket(
                        addr.AddressFamily,
                        sockType,
                        sockProtocol
                        );
                    Console.WriteLine("Client: Socket() is OK...");
                    try
                    {
                        // Create the endpoint that describes the destination
                        destination = new IPEndPoint(addr, remotePort);
                        Console.WriteLine("Client: IPEndPoint() is OK. IP Address: {0}, server port: {1}", addr, remotePort);

                        if ((sockProtocol == ProtocolType.Udp) && (udpConnect == false))
                        {
                            Console.WriteLine("Client: Destination address is: {0}", destination.ToString());
                            break;
                        }
                        else
                        {
                            Console.WriteLine("Client: Attempting connection to: {0}", destination.ToString());
                        }
                        clientSocket.Connect(destination);
                        Console.WriteLine("Client: Connect() is OK...");
                        break;
                    }
                    catch (SocketException)
                    {
                        // Connect failed, so close the socket and try the next address
                        clientSocket.Close();
                        Console.WriteLine("Client: Close() is OK...");
                        clientSocket = null;
                        continue;
                    }
                }

                // Make sure we have a valid socket before trying to use it
                if ((clientSocket != null) && (destination != null))
                {
                    try
                    {
                        // Send the request to the server
                        if ((sockProtocol == ProtocolType.Udp) && (udpConnect == false))
                        {
                            clientSocket.SendTo(sendBuffer, destination);
                            Console.WriteLine("Client: SendTo() is OK...UDP...");
                        }
                        else
                        {
                            /* byte[] btsend = new byte[14];
                             if (textMessage == "1")
                             {
                                 btsend[0] = 1;
                             }
                             if (textMessage == "2")
                             {
                                 btsend[0] = 2;
                             }
                                btsend[0] = (byte)'L';
                                btsend[1] = (byte)'O';
                                btsend[2] = (byte)'G';
                                btsend[3] = (byte)'O';
                                btsend[4] = (byte)' ';
                                btsend[5] = 0x00;
                                btsend[6] = 0x00;
                                btsend[7] = 0x00;
                                btsend[8] = 0x01;
                                btsend[9] = (byte)' ';
                                btsend[10] = (byte)'O';
                                btsend[11] = (byte)'N';
                                btsend[12] = 0x0D;
                                btsend[13] = 0x0A;*/
                            Thread.Sleep(100);
                            rc = clientSocket.Send(sendBuffer);
                            Thread.Sleep(100);
                            //Functionality.AddToEventLog("InscriberControlServer","Client: send() is OK...TCP...",false);
                            Console.WriteLine("Client: Sent request of {0} bytes", rc);

                            // For TCP, shutdown sending on our side since the client won't send any more data
                            if (sockProtocol == ProtocolType.Tcp)
                            {
                                clientSocket.Shutdown(SocketShutdown.Send);
                                Console.WriteLine("Client: Shutdown() is OK...");
                                IPEndPoint fromEndPoint = new IPEndPoint(destination.Address, 0);
                                Console.WriteLine("Client: IPEndPoint() is OK...");
                                EndPoint castFromEndPoint = (EndPoint)fromEndPoint;
                                rc = clientSocket.ReceiveFrom(recvBuffer, ref castFromEndPoint);
                                Console.WriteLine("Client: Read {0} bytes from {1}", recvBuffer[0].ToString(), fromEndPoint.ToString());
                            }
                        }

                        // Receive data in a loop until the server closes the connection. For
                        //    TCP this occurs when the server performs a shutdown or closes
                        //    the socket. For UDP, we'll know to exit when the remote host
                        //    sends a zero byte datagram.
                        while (true)
                        {
                            if ((sockProtocol == ProtocolType.Tcp) || (udpConnect == true))
                            {
                                rc = clientSocket.Receive(recvBuffer);

                                //Console.WriteLine("Client: Receive() is OK...");
                                //Console.WriteLine("Client: Read {0} bytes", rc);
                            }
                            else
                            {
                                IPEndPoint fromEndPoint = new IPEndPoint(destination.Address, 0);
                                Console.WriteLine("Client: IPEndPoint() is OK...");
                                EndPoint castFromEndPoint = (EndPoint)fromEndPoint;
                                rc = clientSocket.ReceiveFrom(recvBuffer, ref castFromEndPoint);
                                Console.WriteLine("Client: ReceiveFrom() is OK...");
                                fromEndPoint = (IPEndPoint)castFromEndPoint;
                                Console.WriteLine("Client: Read {0} bytes from {1}", rc, fromEndPoint.ToString());
                            }

                            // Exit loop if server indicates shutdown
                            if (rc == 0)
                            {
                                clientSocket.Close();
                                Console.WriteLine("Client: Close() is OK...");
                                break;
                            }
                        }
                    }
                    catch (SocketException err)
                    {
                        Console.WriteLine("Client: Error occurred while sending or receiving data.");
                        Console.WriteLine("   Error: {0}", err.Message);
                    }
                }
                else
                {
                    Console.WriteLine("Client: Unable to establish connection to server!");
                }
            }
            catch (SocketException err)
            {
                Console.WriteLine("Client: Socket error occurred: {0}", err.Message);
            }
        }
    }
}
